#! /bin/sh
# PCP QA Test No. 069
# Test pmcd's ability to supress multiple access control warnings from the same
# host
#
# Copyright (c) 1995-2002 Silicon Graphics, Inc.  All Rights Reserved.
#
# check-group-exclude: pminfo
#

seq=`basename $0`
echo "QA output created by $seq"

# get standard filters
. ./common.product
. ./common.filter
. ./common.check

# ideally want a host with only 1 network interface ... getpmcdhosts
# cannot express this, so we used to go for 1 CPU as a likely co-condition,
# but even that has been dropped now that single CPU systems are so
# rare
#
# Need at least PCP 5.3.4 on the remote hosts, so that pmlc there can
# connect to the local pmlogger.
#
eval `./getpmcdhosts -L -n2 -v 'pcp>=5.3.4' | sed -e 's/^/other1=/' -e 's/ / other2=/'`
[ -z "$other1" ] && _notrun "Cannot find first remote host running pmcd"
[ -z "$other2" ] && _notrun "Cannot find second remote host running pmcd"

rm -f $seq.out
_get_libpcp_config
if $ipv6 ; then
    ln $seq.out.ipv6 $seq.out || exit 1
else
    ln $seq.out.nonipv6 $seq.out || exit 1
fi

# real QA test starts here

status=0
signal=$PCP_BINADM_DIR/pmsignal
usersignal=pmsignal	# not run as root, which may lose pcpqa PATH setting
config=$PCP_PMCDCONF_PATH
oconfig=${PCP_PMCDCONF_PATH}.save.$seq
nconfig=$tmp.pmcd.conf.new
log=$PCP_PMCDLOG_PATH
LOCALHOST=`hostname`
LOCALHOST_FULL=`pmhostname`
LOGGING_DIR="$PCP_ARCHIVE_DIR"
_needclean=true


_filter_log()
{
    fixed_width_ip="                        "
    sed <$log \
	-e 's/$/ /' \
	-e '/^00[08]:/d' \
	-e 's/ \[0x[0-9a-f]*]//' \
	-e 's/ \[(nil)]//' \
	-e "s,$PCP_RUN_DIR,PCP_RUN_DIR,g" \
	-e "/$LOCALHOST\$/s/$LOCALHOST\$/ME/" \
	-e "s/ 127\.0\.0\.1 / MY_IP /" \
	-e "s/ ::1 / MY_IP /" \
	-e "s/ $me_ip / MY_IP /" \
	-e "s/ $other1_ip / OTHER1_IP /" \
	-e "s/ $other2_ip / OTHER2_IP /" \
	-e "s/ \(OTHER._IP\) *\( 255.255.255.255\) / \1 ${fixed_width_ip} \2 /" \
	-e '/access violation .* [0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9]/{
N
d
}' \
	-e "s/ $me_xip / MY_HEXIP /" \
	-e "s/ $other1 / OTHER_1 /" \
	-e "s/ $other1_xip / OTHER1_HEXIP /" \
	-e "s/ $other2 / OTHER_2 /" \
	-e "s/ $other2_xip / OTHER2_HEXIP /" \
	-e 's/fd [0-9-][0-9]*/fd <n>/g' \
	-e '/client\[[0-9][0-9]*\]/s//client[N]/' \
	-e '/lib=.*\.'"$DSO_SUFFIX"'/s/[0-9] dso/N dso/' \
	-e "/Warning:/s/PMCD_PORT=$port/PMCD_PORT=\$port/" \
	-e '/ContactAgents: agent/d' \
	-e 's/ $//' \
    | $PCP_AWK_PROG '
BEGIN				{ skip = 0 }
/client connection from/	{ print; print "+++"; skip=1; next }
skip == 1 && NF == 0		{ skip = 0 }
skip == 1			{ next }
				{ print }' \
    | _filter_pmcd_log | \
    sed \
	-e '/UNIX_DOMAIN_SOCKET/d' \
	-e '/unix:/d' \
    | $PCP_AWK_PROG '
BEGIN					{ seen_my_access = 0 }
/access violation from host MY_IP/	{ seen_my_access++
					  if (seen_my_access > 1) skip=2
					}
skip > 0				{ skip--; next }
					{ print }'
    # Note on last awk component ...
    # depending on the IPv6 support status, can see multiple lines for
    # 127.0.0.1 _and_ ::1 ... only need to report one occurrence of
    # the filtered lines
    #

}

_cleanup()
{
    if $_needclean
    then
        _needclean=false
	if [ -f $oconfig ]
	then
	    $sudo cp $oconfig $config
	    $sudo rm -f $oconfig
	fi
	unset PMCD_PORT # don't worry about preserving just get rid of it
	pmafm $LOGGING_DIR/$LOCALHOST/Latest remove >$tmp.cmd 2>&1 \
	&& $sudo sh $tmp.cmd
	echo "Restarting pmcd"
	_service pmcd restart 2>&1 | _filter_pcp_restart
	_wait_for_pmcd
	_restore_auto_restart pmcd
	_service pmlogger restart 2>&1 | _filter_pcp_restart
	_wait_for_pmlogger
    fi
    rm -f $tmp.*
}

_interrupt()
{
    trap 1 2 3 15
    echo "Interrupted"
    _exit 1
}

trap "_interrupt; exit \$status" 1 2 3 15	# refactor-ok
trap "_cleanup; exit \$status" 0

_stop_auto_restart pmcd
if ! _service pmlogger stop 2>&1; then _exit 1; fi \
| _filter_pcp_stop
_wait_pmlogger_end || _exit 1
if ! _service pmcd stop 2>&1; then _exit 1; fi \
| _filter_pcp_stop
_wait_pmcd_end || _exit 1

cat <<End-of-File >$tmp.c
#include <pcp/pmapi.h>
#ifdef HAVE_NETWORK_BYTEORDER
yes
#else
no
#endif
End-of-File

network_byteorder=`cc -E $tmp.c | sed -e '/^ *$/d' | tail -1`

_get_hex_addr()
{
    if [ $network_byteorder = yes ]
    then
	echo $1 \
	| $PCP_AWK_PROG -F '.' '{ printf "%02x%02x%02x%02x", $1, $2, $3, $4 }'
    else
	echo $1 \
	| $PCP_AWK_PROG -F '.' '{ printf "%02x%02x%02x%02x", $4, $3, $2, $1 }'
    fi
}

# Number of clients is platform/config dependent ...
# 1. pmie
# 2. pmie (maybe)
# 3. pmlogger from pmfind (maybe)
#
_filter_numclients()
{
    $PCP_AWK_PROG '
$1 == "value"	{ if ($2 == 1 || $2 == 2 || $2 == 3) $2 = "1..3" }
		{ print }'
}

pmafm $LOGGING_DIR/$LOCALHOST/Latest remove >$tmp.cmd 2>&1 \
&& $sudo sh $tmp.cmd

echo "host1 = $other1" >>$seq_full
echo "host2 = $other2" >>$seq_full

# pick a tcp port that is not in use
#
port=`_get_port tcp 4340 4350`
if [ -z "$port" ]
then
    echo "Arrgh ... no free TCP port in the range 4340 ... 4350"
    exit 1
fi
echo "port=$port" >>$seq_full

me_ip=`_host_to_ipaddr $LOCALHOST`
me_xip=`_get_hex_addr $me_ip`
other1_ip=`_host_to_ipaddr $other1`
other1_xip=`_get_hex_addr $other1_ip`
other2_ip=`_host_to_ipaddr $other2`
other2_xip=`_get_hex_addr $other2_ip`

echo "me: ip=$me_ip hex_ip=$me_xip" >>$seq_full
echo "host1: ip=$other1_ip hex_ip=$other1_xip" >>$seq_full
echo "host2: ip=$other2_ip hex_ip=$other2_xip" >>$seq_full

echo "# Installed by PCP QA test $seq on `date`" >$nconfig
grep -E '^(linux|darwin|solaris|freebsd|openbsd)' $config >>$nconfig
grep '^pmcd' $config >>$nconfig

$sudo cp $config $oconfig
$sudo cp $nconfig $config
echo "--- initial pmcd.conf ---" >>$seq_full
cat $config >>$seq_full

# need to start pmcd indirectly in a shell script because sudo
# may cleanse the environment, which also means we cannot use
# the _service wrapper
#
export PMCD_CONNECT_TIMEOUT=30
export PMCD_PORT=$port
echo "export PMCD_CONNECT_TIMEOUT=$PMCD_CONNECT_TIMEOUT" >$tmp.start
echo "export PMCD_PORT=$PMCD_PORT" >>$tmp.start
echo "$PCP_RC_DIR/pmcd restart" >>$tmp.start
$sudo sh $tmp.start 2>&1 \
| tee -a $seq_full \
| sed \
    -e '/Wait/d' \
| _filter_pcp_start
_wait_for_pmcd || _exit 1

echo "should be OK"
pminfo -f pmcd.numclients | _filter_numclients

sleep 2		# make sure pmcd.conf timestamp changes

cat <<End-of-File >>$nconfig

[access]
allow $other1: all except store, maximum 1 connections;
allow $other2: all, maximum 1 connections;
disallow *: all;
End-of-File
$sudo cp $nconfig $config

echo "=== local pmcd.conf ===" >>$seq_full
cat $config >>$seq_full

$sudo $signal -a -s HUP pmcd
sleep 5

echo
echo "expect two access control errors:"
# For Linux sometimes see "Connection reset by peer" ... this is believed
# to be a timing issue, and the results are semantically equivalent for
# the purposes of this test, so ...
# Ditto for "Connection refused" on more recent platform versions.
#
pminfo -f pmcd.numclients 2>&1 \
| sed \
    -e "s/\"local:\"/\"LOCALHOST\"/" \
    -e 's/"'$LOCALHOST'"/"LOCALHOST"/' \
    -e 's/Connection reset by peer/No permission to perform requested operation/' \
    -e 's/Connection refused/No permission to perform requested operation/' \
# end
pminfo -f pmcd.numclients 2>&1 \
| sed \
    -e "s/\"local:\"/\"LOCALHOST\"/" \
    -e 's/"'$LOCALHOST'"/"LOCALHOST"/' \
    -e 's/Connection reset by peer/No permission to perform requested operation/' \
    -e 's/Connection refused/No permission to perform requested operation/' \
# end
echo

echo "expect two connection limit errors:"
cmd1='sh -c "PMCD_CONNECT_TIMEOUT=30 PMCD_PORT='$PMCD_PORT'; export PMCD_CONNECT_TIMEOUT PMCD_PORT; pmval -h '$LOCALHOST_FULL' pmcd.numclients"'
echo "cmd1: $cmd1" >>$seq_full
(ssh -q pcpqa@$other1 $cmd1  &) 1>$tmp.pmval 2>&1
sleep 5

# not sure how long it takes to get the remote pmval command started,
# and connected to pmcd so be prepared to try a few times ...
#
cmd2='sh -c "PMCD_CONNECT_TIMEOUT=30 PMCD_PORT='$PMCD_PORT'; export PMCD_CONNECT_TIMEOUT PMCD_PORT; pminfo -h '$LOCALHOST_FULL' -f pmcd.numclients"'
echo "cmd2: $cmd2" >>$seq_full
cnt=0
echo >$tmp.pminfo
for i in 1 2 3 4 5 6 7 8 9 10
do
    ssh -q pcpqa@$other1 $cmd2 >$tmp.tmp 2>&1
    echo "--- attempt $i from $other1 ---" >>$tmp.pminfo
    cat $tmp.tmp >>$tmp.pminfo
    if grep 'connection limit' $tmp.tmp >/dev/null
    then
	# bingo!
	sed -e 's/".*"/"OTHERHOST1"/' <$tmp.tmp
	cnt=`expr $cnt + 1`
	[ $cnt -eq 2 ] && break
    fi
    sleep 1
done
if [ $cnt -ne 2 ]
then
    cat $tmp.pminfo
fi
ssh -q pcpqa@$other1 $usersignal -a pmval > /dev/null 2>&1
echo
echo "=== pmval output ===" >>$seq_full
cat $tmp.pmval >>$seq_full
echo "=== pminfo output ===" >>$seq_full
cat $tmp.pminfo >>$seq_full

echo "expect two connection limit errors:"
(ssh -q pcpqa@$other2 $cmd1 &) > $tmp.pmval 2>&1
sleep 5

# not sure how long it takes to get the remote pmval command started
# and connected to pmcd so be prepared to try a few times ...
#
found=false
echo >$tmp.pminfo
for i in 1 2 3 4 5 6 7 8 9 10
do
    ssh -q pcpqa@$other2 $cmd2 >$tmp.tmp 2>&1
    echo "--- attempt $i from $other2 ---" >>$tmp.pminfo
    cat $tmp.tmp >>$tmp.pminfo
    if grep 'connection limit' $tmp.tmp >/dev/null
    then
	# bingo!
	sed -e 's/".*"/"OTHERHOST2"/' <$tmp.tmp
	found=true
	break
    fi
    sleep 1
done
$found || cat $tmp.pminfo
#
# Make this one a store to see if connection and access errors are treated the
# same way.  Note that pmstore fails with connection limit exceeded before it
# gets to attempt the store.
#
cmd3='sh -c "PMCD_CONNECT_TIMEOUT=30 PMCD_PORT='$PMCD_PORT'; export PMCD_CONNECT_TIMEOUT PMCD_PORT; pmstore -h '$LOCALHOST_FULL' pmcd.control.debug \"pdu\""'
echo "cmd3: $cmd3" >>$seq_full
# not sure how long it takes to get the remote pmval command started
# and connected to pmcd so be prepared to try a few times ...
#
found=false
echo >$tmp.pmstore
for i in 1 2 3 4 5 6 7 8 9 10
do
    ssh -q pcpqa@$other2 $cmd3 >$tmp.tmp 2>&1
    echo "--- attempt $i from $other2 ---" >>$tmp.pminfo
    cat $tmp.tmp >>$tmp.pmstore
    if grep 'connection limit' $tmp.tmp >/dev/null
    then
	# bingo!
	sed -e 's/".*"/"OTHERHOST2"/' <$tmp.tmp
	found=true
	break
    fi
    sleep 1
done
$found || cat $tmp.pmstore
ssh -q pcpqa@$other2 $usersignal -a pmval > /dev/null 2>&1
echo
echo "=== pmval output ===" >>$seq_full
cat $tmp.pmval >>$seq_full
echo "=== pminfo output ===" >>$seq_full
cat $tmp.pminfo >>$seq_full
echo "=== pmstore output ===" >>$seq_full
cat $tmp.pmstore >>$seq_full

echo 'expect 3 access violation messages for localhost, OTHERHOST1 and OTHERHOST2
and one endclient access violation for localhost and one endclient connection
limit for each of OTHERHOST1 and OTHERHOST2'
_filter_log

echo "=== pmcd.log ===" >>$seq_full
cat $log >>$seq_full

echo
echo "If failure, check $seq.full"
